#ifndef __MY_UTIL__
#define __MY_UTIL__
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <sw/redis++/redis++.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <memory>
#include <unistd.h>
#include <experimental/filesystem>
#include <jsoncpp/json/writer.h>
#include <jsoncpp/json/reader.h>
#include <iconv.h>
#include <mysql/mysql.h>
#include "../Log_system/Log.h"
#include "bundle.h"

using namespace std;
const string HOST = "mysql";
const string USER = "TCY";
const string PASSWD = "123456";
const string DB = "Cloud_memory";
unsigned int PORT = 3306;
namespace fs = std::experimental::filesystem;
namespace Cloud
{
    class FileUtil // 文件工具类
    {
    private:
        string _filename;

    public:
        FileUtil(const string &filename) : _filename(filename)
        {
        }
        struct stat st;     // 存储文件信息
        uint64_t FileSize() // 获取文件大小
        {
            if (stat(_filename.c_str(), &st) < 0)
            {
                WARN("%s", "get Filesize fail!");
                return -1;
            }

            return st.st_size;
            // try
            // {
            //     auto size = fs::file_size(_filename);
            //     std::cout << "File size: " << size << " bytes\n";
            //     return size;
            // }
            // catch (const fs::filesystem_error &e)
            // {
            //     std::cerr << "Error: " << e.what() << '\n';
            //     // 可以在这里添加更多的错误处理逻辑
            // }
            // return fs::file_size(_filename);
        }

        time_t LastMTime() // 获取文件最后一次修改时间
        {
            if (stat(_filename.c_str(), &st) < 0)
            {
                WARN("%s", "get fileModifi time fail!");
                return -1;
            }
            return st.st_mtim.tv_sec;
        }

        time_t LastATime() // 获取文件最后一次访问时间
        {
            if (stat(_filename.c_str(), &st) < 0)
            {
                WARN("%s", "get fileaccess time fail!");
                return -1;
            }
            return st.st_atim.tv_sec;
        }

        string GetFileName() // 获取文件路径下的文件名
        {
            // size_t pos = _filename.find_last_of("/");
            // if (pos == string::npos)
            // {
            //     return _filename;
            // }
            // return _filename.substr(pos + 1);
            // DEBUG("%s%s", "FileName: ", fs::path(_filename).filename().string().c_str());
            return fs::path(_filename).filename().string();
        }

        bool GetPosLen(string *content, size_t pos, size_t len) // 读取指定位置文件内容
        {
            size_t fszie = FileSize();
            if (pos + len > fszie)
            {
                ERROR("%s", "get file len is fail");
                return false;
            }
            ifstream ifs(_filename, ios::binary);
            if (ifs.is_open() == false)
            {
                ERROR("%s", "read open file fail");
                return false;
            }
            ifs.seekg(pos, ios::beg);
            content->resize(len); // 减少扩容带来的内存分配和复制的开销
            ifs.read(&(*content)[0], len);
            if (ifs.good() == false) // 判断文件是否读取
            {
                ERROR("%s", "GetPosLen file read fail");
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        bool GetCentent(string *content) // 读取文件内容
        {
            return GetPosLen(content, 0, FileSize());
        }

        bool SetCentent(string &content) // 写入文件
        {
            ofstream ofs;
            ofs.open(_filename, ios::binary);
            if (ofs.is_open() == false)
            {
                ERROR("%s", "write open file fail");
                return false;
            }

            ofs.write(&content[0], content.size());
            if (ofs.good() == false) // 判断文件是否写入成功
            {
                ERROR("%s", "SetCentent file read fail");
                ofs.close();
                return false;
            }

            ofs.close();

            return true;
        }

        // bool Compress(const string &packname) // 压缩文件--->持久化到磁盘
        // {
        //     // 1、获取源文件数据
        //     string body;
        //     if (GetCentent(&body) == false)
        //     {
        //         ERROR("%s", "Compress get file content failed");
        //         return false;
        //     }

        //     // 2、将数据进行压缩
        //     DEBUG("%s", "File compression is running");
        //     string packed = bundle::pack(bundle::LZIP, body); // 压缩文件

        //     // 3.1、将压缩的数据写入文件
        //     Cloud::FileUtil fu(packname);
        //     if (fu.SetCentent(packed) == false) // 将压缩后的文件写入指定文件中
        //     {
        //         ERROR("%s", "Compress write file failed");
        //         return false;
        //     }
        //     INFO("%s%s", GetFileName().c_str(), " Compressed");
        //     return true;
        // }

        bool Compress(const string &packname) // 压缩文件---->持久化到数据库
        {
            // 1、获取源文件数据
            string body;
            if (GetCentent(&body) == false)
            {
                WARN("%s", "Compress get file content failed");
                return false;
            }

            // 2、将数据进行压缩
            DEBUG("%s", "File compression is running");
            string packed = bundle::pack(bundle::LZIP, body); // 压缩文件

            // 3、将压缩的数据写入mysql
            MYSQL *my = mysql_init(nullptr); // 初始化数据库
            if (nullptr == my)
            {
                ERROR("%s", "Mysql init failed");
                return false;
            }
            INFO("%s", "Mysql init scuccess");
            if (mysql_real_connect(my, HOST.c_str(), USER.c_str(), PASSWD.c_str(), DB.c_str(), PORT, nullptr, 0) == nullptr)
            {
                WARN("%s", "Mysql connect failed");
                mysql_close(my);
                return false;
            }
            INFO("%s", "Mysql connect success");

            // 构造INSERT语句
            std::string sql = "INSERT INTO compressed_files (filename, filedata) VALUES (?, ?)";
            MYSQL_STMT *stmt = mysql_stmt_init(my);
            if (!stmt)
            {
                WARN("%s", "mysql_stmt_init failed");
                mysql_close(my);
                return false;
            }

            // 识别为需要绑定的参数
            if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length()))
            {
                WARN("%s%s", "mysql_stmt_prepare failed: ", mysql_stmt_error(stmt));
                mysql_stmt_close(stmt);
                mysql_close(my);
                return false;
            }

            // 绑定参数
            MYSQL_BIND bind[2];
            memset(bind, 0, sizeof(bind));

            size_t filename_length = _filename.size();
            // 绑定 filename
            bind[0].buffer_type = MYSQL_TYPE_STRING;
            bind[0].buffer = (char *)_filename.data();
            bind[0].buffer_length = _filename.size();
            bind[0].length = &filename_length;

            size_t packdata_length = packed.size();
            // 绑定 filedata
            bind[1].buffer_type = MYSQL_TYPE_BLOB;
            bind[1].buffer = (char *)packed.data();
            bind[1].buffer_length = packed.size();
            bind[1].length = &packdata_length;

            // 将具体的值绑定到占位符上
            if (mysql_stmt_bind_param(stmt, bind))
            {
                WARN("%s%s", "mysql_stmt_bind_param failed: ", mysql_stmt_error(stmt));
                mysql_stmt_close(stmt);
                mysql_close(my);
                return false;
            }

            // 执行SQL语句
            if (mysql_stmt_execute(stmt))
            {
                WARN("%s%s", "mysql_stmt_execute failed: ", mysql_stmt_error(stmt));
            }
            mysql_stmt_close(stmt);
            mysql_close(my);

            INFO("%s%s", GetFileName().c_str(), " Compressed");
            return true;
        }

        // 从mysql拉取数据
        bool UnCompress() // 解压文件
        {
            DEBUG("%s", "File Uncompression is running");

            MYSQL *my = mysql_init(nullptr);
            if (nullptr == my)
            {
                ERROR("%s", "mysql init failed");
                return false;
            }
            INFO("%s", "mysql init successed");

            if (mysql_real_connect(my, HOST.c_str(), USER.c_str(), PASSWD.c_str(), DB.c_str(), PORT, nullptr, 0) == nullptr)
            {
                ERROR("%s", "mysql connect failed");
                mysql_close(my);
                return false;
            }
            INFO("%s", "mysql connect successed");

            string sql = "SELECT filedata FROM compressed_files WHERE filename = '" + _filename + "'";
            INFO("%s", sql.c_str());

            int n = mysql_query(my, sql.c_str());
            if (n == 0)
            {
                INFO("%s", "mysql exec select successed");
            }
            else
            {
                WARN("%s", "mysql exec select failed");
                mysql_close(my);
                return false;
            }

            MYSQL_RES *result = mysql_store_result(my);
            if (!result)
            {
                ERROR("%s%s", "mysql_store_result failed: ", mysql_error(my));
                mysql_close(my);
                return -1;
            }

            MYSQL_ROW row;
            // std::ofstream file(Unpackname, std::ios::binary);
            bool data_found = false;

            // while ((row = mysql_fetch_row(result)))
            // {
            //     unsigned long *lengths = mysql_fetch_lengths(result);
            //     if (!file.is_open())
            //     {
            //         ERROR("%s", "Failed to open file for writing");
            //         mysql_free_result(result);
            //         mysql_close(my);
            //         file.close();
            //         return false;
            //     }
            //     file.write(row[0], lengths[0]);
            //     data_found = true;
            // }
            string body;
            if ((row = mysql_fetch_row(result)))
            {
                unsigned long *lengths = mysql_fetch_lengths(result);
                body.append(row[0], lengths[0]); // 将数据追加到字符串
                data_found = true;
            }

            // 检查是否找到数据
            if (!data_found)
            {
                ERROR("%s", "No specified field data found");
            }
            // file.close();
            mysql_free_result(result);
            mysql_close(my);
            string unpacked = bundle::unpack(body); // 解压文件

            // 将数据存储到Redis，设置过期时间为1小时
            try {
                // 创建Redis连接
                auto redis = sw::redis::Redis("tcp://redis:6379/1");
                string ret = redis.ping();
                if(ret=="PONG")
                    INFO("Redis Init Complate,Establish a link");
                
                // 将文件内容存储到Redis并设置过期时间为1小时（3600秒）
                redis.set(_filename.c_str(), sw::redis::StringView(unpacked.c_str(), unpacked.size()),std::chrono::seconds(3600));
                INFO("Redis Inserted a piece of Data,%s,Expiration date is 3600s",_filename.c_str());
            } catch (const std::exception &e) {
                WARN("Redis operation failed: %s", e.what());
                return false;
            }
            if (SetCentent(unpacked) == false) // 将解压后的文件写入指定文件中
            {
                ERROR("%s", "UnCompress write file failed");
                return false;
            }
            DEBUG("%s%s", GetFileName().c_str(), " UnCompressed");
            return true;
        }

        // 》》》》》从磁盘获取数据解压
        // bool UnCompress(string &Unpackname) // 解压文件
        // {
        //     string body;
        //     DEBUG("%s", "File Uncompression is running");
        //     if (GetCentent(&body) == false)
        //     {
        //         ERROR("%s", "UnCompress get file content failed");
        //         return false;
        //     }

        //     string unpacked = bundle::unpack(body); // 解压文件
        //     Cloud::FileUtil fu(Unpackname);

        //     if (fu.SetCentent(unpacked) == false) // 将解压后的文件写入指定文件中
        //     {
        //         ERROR("%s", "UnCompress write file failed");
        //         return false;
        //     }
        //     DEBUG("%s%s", GetFileName().c_str(), " UnCompressed");
        //     return true;
        // }

        bool Exists() // 判断文件是否存在
        {
            return fs::exists(_filename);
        }

        bool CreateDirectory() // 创建目录
        {
            if (Exists())
                return true;
            if (!fs::create_directories(_filename))
            {
                ERROR("%s", "CreateDirectory is fail");
                return false;
            }
            return true;
        }
        bool ScanDirectory(vector<string> *array) // 扫描目录中的文件
        {
            for (auto &e : fs::directory_iterator{_filename})
            {
                if (fs::is_directory(e)) // 判断当前文件是否为目录
                    continue;
                array->push_back(fs::path(e).relative_path().string()); // 将当前文件的相对路径添加到vector
            }
            return true;
        }
        bool Remove()
        {
            if (Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }
    };

    class JsonUtil // json工具类
    {
    public:
        static bool Serialize(const Json::Value &root, string *str)
        {
            Json::StreamWriterBuilder swb;
            unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            stringstream val;
            // DEBUG("%s", " Serialize is Running ");
            if (sw->write(root, &val) != 0)
            {
                WARN("%s", " Serialize failed ");
                return false;
            }

            *str = val.str();
            return true;
        }
        static bool UnSerialize(const string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            unique_ptr<Json::CharReader> cr(crb.newCharReader());
            string error;
            // DEBUG("%s", " UnSerialize is Running ");
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &error);
            if (ret == false)
            {
                WARN("%s%s", "parse error:", error);
                return false;
            }
            return false;
        }
    };
}

#endif